
/*
 * Author: Benedikt Sauter <sauter@ixbat.de>
 * All rights reserved.
 *
 * Short descripton of file:
 *
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution.
 *   * Neither the name of the FH Augsburg nor the names of its 
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>

#include "main.h"
#include "common.h"
#include "protocol.h"

#include "spi.h"

static uint8_t spi_init (void)
{
	PORTB &= ~((1 << SCK) | (1 << MISO) | (1 << MOSI));	// SCK have to be low in IDLE
	DDRB &= ~(1 << MISO);
	DDRB = (1 << MOSI) | (1 << SCK);

	SPCR = 0;
	SPSR = 0;

	SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0) | (1 << SPR1);

	octopus.ports[5] = PIN_SCK;
	octopus.ports[6] = PIN_MOSI;
	octopus.ports[7] = PIN_MISO;

	return RSP_OK;
}

static uint8_t spi_deinit (void)
{
	SPCR = 0;
	SPSR = 0;

	octopus.ports[5] = PIN_OUT;	// this is wrong!
	octopus.ports[6] = PIN_OUT;	// this is wrong!
	octopus.ports[7] = PIN_OUT;	// this is wrong!

	return RSP_OK;
}

//static uint8_t spi_set_speed (uint8_t speed)
//{
//	return RSP_OK;
//}

static uint8_t spi_send (const uint8_t *txbuf, uint8_t length)
{
	while (length--)
	{
		SPDR = *txbuf++;
		while (!(SPSR & (1 << SPIF))) ;
	}
	return RSP_OK;
}

static uint8_t spi_receive (uint8_t *rxbuf, uint8_t length)
{
	int timeout = 0;		// KH: timeout adds up over all bytes received ??

	while (length--)
	{
		SPDR = 0;
		while (!(SPSR & (1 << SPIF)))
		{
			timeout++;
			if (timeout > 1000)
				return RSP_TIMEOUT;
		}
		*rxbuf++ = SPDR;
	}
	return RSP_OK;
}

static uint8_t spi_send_and_receive (const uint8_t *txbuf, uint8_t *rxbuf, uint8_t length)
{
	int timeout = 0;		// KH: timeout adds up over all bytes received ??

	while (length--)
	{
		SPDR = *txbuf++;
		while (!(SPSR & (1 << SPIF)))
		{
			timeout++;
			if (timeout > 1000)
				return RSP_TIMEOUT;
		}
		*rxbuf++ = SPDR;
	}
	return RSP_OK;
}

//////////////////////////////////////////////////////////////////////////////

static void spi_deinit_usb (void)
{
	answer[0] = CMD_SPI_DEINIT;
	answer[1] = spi_deinit ();
	answer[2] = 0;
	CommandAnswer (3);
}

static void spi_set_speed_usb (uint8_t speed)
{

}

static void spi_send_usb (const uint8_t *txbuf, uint8_t length)
{
	answer[0] = CMD_SPI_SEND;
	answer[1] = spi_send (txbuf, length);
	answer[2] = 0;
	CommandAnswer (3);
}

static void spi_receive_usb (uint8_t length)
{
	answer[0] = CMD_SPI_RECV;
	answer[1] = spi_receive (&answer[2], length);
	answer[2] = 0;
	CommandAnswer (length + 2);
}

static void spi_send_and_receive_usb (const uint8_t *txbuf, uint8_t length)
{
	answer[0] = CMD_SPI_SEND;
	answer[1] = spi_send_and_receive (txbuf, &answer[2], length);
	answer[2] = 0;
	CommandAnswer (2 + length);
}

static void spi_init_usb (void)
{
	answer[0] = CMD_SPI_INIT;
	answer[1] = spi_init ();
	answer[3] = 0;
	CommandAnswer (3);
}

//////////////////////////////////////////////////////////////////////////////

void spi_parser (const uint8_t *buf)
{
	switch (buf[0])
	{
	case CMD_SPI_INIT:
		spi_init_usb ();
		break;
	case CMD_SPI_DEINIT:
		spi_deinit_usb ();
		break;
	case CMD_SPI_SPEED:
		spi_set_speed_usb (buf[2]);
		break;
	case CMD_SPI_SEND:
		spi_send_usb (&buf[2], buf[1]);
		break;
	case CMD_SPI_RECV:
		spi_receive_usb (buf[1]);
	case CMD_SPI_SEND_AND_RECV:
		spi_send_and_receive_usb (&buf[2], buf[1]);
		break;
	default:
		answer[0] = buf[0];
		answer[1] = RSP_UNKOWN_CMD;
		answer[2] = 0;
		CommandAnswer (3);
	}
}
